package per.wsj.commonlib.pravicy;

import android.content.ClipboardManager;
import android.content.Context;
import android.os.Build;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import per.wsj.commonlib.utils.LogUtil;

public class ClipboardHacker {

    public static void hook(Context context) {
        try {
            Class<?> clipboardClass = Class.forName("android.content.IClipboard");

            Field sServiceField;
            Object sService;
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                sServiceField = ClipboardManager.class.getDeclaredField("mService");
                sServiceField.setAccessible(true);
                ClipboardManager cm = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
                sService = sServiceField.get(cm);

                Object proxyInstance = Proxy.newProxyInstance(Thread.class.getClassLoader(), new Class[]{clipboardClass}, new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        insertLogic(method);
                        return method.invoke(sService, args);
                    }
                });

                sServiceField.set(cm, proxyInstance);
            } else {
                sServiceField = ClipboardManager.class.getDeclaredField("sService");
                sServiceField.setAccessible(true);
//            Object sService = sServiceField.get(null);
//            LogUtils.e("sService: " + sService);
                Method getServiceMethod = ClipboardManager.class.getDeclaredMethod("getService");
                getServiceMethod.setAccessible(true);
                sService = getServiceMethod.invoke(null);
                LogUtil.e("sService: " + sService);

                Object proxyInstance = Proxy.newProxyInstance(Thread.class.getClassLoader(), new Class[]{clipboardClass}, new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        insertLogic(method);
                        return method.invoke(sService, args);
                    }
                });

                sServiceField.set(null, proxyInstance);
            }
        } catch (Exception e) {
            e.printStackTrace();
            LogUtil.e("exp: " + e);
        }
    }

    private static void insertLogic(Method method) {
        LogUtil.e("method: " + method.getName());
        if (method.getName().equals("getPrimaryClip")) {
//                        LogUtils.e("get: " + args.length);
            String stackTrace = getTargetStackTrace();
            LogUtil.e("get: " + stackTrace);
        } else if (method.getName().equals("addPrimaryClipChangedListener")) {
//                        ClipData arg2 = (ClipData) args[0];
//                        LogUtils.e("set: " + arg2.getItemAt(0).getText());
            String stackTrace = getTargetStackTrace();
            LogUtil.e("set: " + stackTrace);
        } else if (method.getName().equals("hasPrimaryClip")) {
            String stackTrace = getTargetStackTrace();
            LogUtil.e("has: " + stackTrace);
        }
    }

    private static String getTargetStackTrace() {
        StringBuffer sb = new StringBuffer();
        sb.append("+++++++++++++++++++++++++start++++++++++++++++++++++++++").append("\n");
        StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
        for (StackTraceElement stackTraceElement : stackTrace) {
            if (stackTraceElement.toString().startsWith("android.view.ViewGroup")) {
                continue;
            }
            sb.append(stackTraceElement.toString()).append("\n");
        }
        sb.append("--------------------------end-------------------------").append("\n");
        return sb.toString();
    }
}
